%%%-------------------------------------------------------------------
%% @doc shield_words public API
%% @end
%%%-------------------------------------------------------------------

-module(shield_words).
-behaviour(gen_server).

-include("debug.hrl").

-export([start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).


-export([start/0]).
-export([stop/0]).
-export([filter/1]).

%% Local functions
-export([do_filter/2]).

-define(SHIELDWORD, "*").
-define(SHIELDWORDS, "***").

%% ====================================================================
%% API functions
%% ====================================================================
start() ->
    ?MODULE:start_link().

stop() ->
    ok.

filter(Msg) ->
    gen_server:call(?MODULE, {filter, Msg}).

%% ====================================================================
%% External functions
%% ====================================================================
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).


%% ====================================================================
init([]) ->
    {ok, []}.


handle_call(Info, From, State) ->
    try do_call(Info, From, State)
    catch
        Class:Reason:Stack ->
            ?ERROR("handle_call error:~n~p~nstate:~n~p:~nInfo:~p", [{Class, Reason, Stack}, State, Info]),
            {reply, fail, State}
    end.


handle_cast(Info, State) ->
    try do_cast(Info, State)
    catch
        Class:Reason:Stack ->
            ?ERROR("handle_cast error:~n~p~nstate:~n~p:~nInfo:~p", [{Class, Reason, Stack}, State, Info]),
            {noreply, State}
    end.


handle_info(Info, State) ->
    try do_info(Info, State)
    catch
        Class:Reason:Stack ->
            ?ERROR("handle_info error:~n~p~nstate:~n~p:~nInfo:~p", [{Class, Reason, Stack}, State, Info]),
            {noreply, State}
    end.


terminate(_Reason, _Status) ->
    ok.


code_change(_oldvsn, Status, _extra) ->
    {ok, Status}.


%% ====================================================================
%% Local functions
%% ====================================================================
do_call({filter, Msg}, From, State) ->
    spawn_link(?MODULE, do_filter, [From, Msg]),
    {noreply, State};

do_call(_Info, _From, State) ->
    ?trace(_Info),
    {reply, ok, State}.


%% --------------------------------------------------------------------
do_cast(_Info, State) ->
    ?trace(_Info),
    {noreply, State}.


%% --------------------------------------------------------------------
do_info(_Info, State) ->
    ?trace(_Info),
    {noreply, State}.


%% --------------------------------------------------------------------
do_filter(From, Msg) ->
    try 
        Str = do_filter1(Msg),
        Utf8BinStr = unicode:characters_to_binary(Str, utf8, unicode),
        gen_server:reply(From, Utf8BinStr)
    catch
        Class:Reason:Stack ->
            ?trace("shield_words error ~nClass:~p, ~nReason:~p, ~nStack:~p", [Class, Reason, Stack]),
            gen_server:reply(From, ?SHIELDWORDS)
    end.

do_filter1([]) -> [];
do_filter1(Utf8Msg) when is_binary(Utf8Msg) -> do_filter2(unicode:characters_to_list(Utf8Msg, unicode));
do_filter1(Utf8Msg) when is_list(Utf8Msg) -> do_filter2(unicode:characters_to_list(unicode:characters_to_binary(Utf8Msg), unicode));
do_filter1(_Utf8Msg) -> throw("input error, not string").

do_filter2([]) -> [];
do_filter2([?SHIELDWORD | Tail]) ->
    %% 已经是屏蔽字了，检查后面的
    [?SHIELDWORD | do_filter2(Tail)];
do_filter2([Head | Tail]) ->
    case shield_words_config:words([Head]) of
        null ->
            %% 不是屏蔽字，检查后面的
            [Head | do_filter2(Tail)];
        List ->
            case do_filter3([Head | Tail], List) of
                false ->
                    %% 不是屏蔽短语，检查后面的
                    [Head | do_filter2(Tail)];
                [ConvertedHead | ConvertedTail] ->
                    %% 是屏蔽字或者屏蔽短语，继续检查后面的
                    [ConvertedHead | do_filter2(ConvertedTail)]
            end
    end.

do_filter3(_Msg, []) -> false;
do_filter3(Msg, [ShieldWords | List]) ->
    case do_filter4(Msg, ShieldWords) of
        false ->
            %% 不是当前屏蔽字，继续下一组
            do_filter3(Msg, List);
        ConvertedMsg ->
            %% 是屏蔽字或者屏蔽短语，返回屏蔽后的消息
            ConvertedMsg
    end.

do_filter4(Msg, []) -> ?SHIELDWORDS ++ Msg;     %% 定位到屏蔽字，完成替换
do_filter4([Head | Tail], [Head | ShieldWords]) -> do_filter4(Tail, ShieldWords);
do_filter4(_Msg, _ShieldWords) -> false.        %% 没有检测到屏蔽字
